import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

import 'NestedScrollViewDemo.dart';

///控件滚动监听
class CustomScrollControllerTestRoute extends StatefulWidget {
  @override
  CustomScrollControllerTestRouteState createState() {
    return CustomScrollControllerTestRouteState();
  }
}

class CustomScrollControllerTestRouteState
    extends State<CustomScrollControllerTestRoute> {
  ///普通嵌套
  Widget buildTwoListView() {
    var listView = ListView.builder(
      itemCount: 20,
      shrinkWrap: true,
      itemBuilder: (_, index) => ListTile(title: Text('$index')),
    );
    return Column(
      children: [
        Expanded(flex: 1, child: listView),
        Divider(color: Colors.grey),
        Expanded(flex: 1, child: listView),
      ],
    );
  }

  ///嵌套滑动
  Widget buildTwoSliverList() {
    // SliverFixedExtentList 是一个 Sliver，它可以生成高度相同的列表项。
    // 再次提醒，如果列表项高度相同，我们应该优先使用SliverFixedExtentList
    // 和 SliverPrototypeExtentList，如果不同，使用 SliverList.
    var listView = SliverFixedExtentList(
      itemExtent: 26, //列表项高度固定
      delegate: SliverChildBuilderDelegate(
        (_, index) => ListTile(title: Text('$index')),
        childCount: 10,
      ),
    );
    // 使用
    return CustomScrollView(
      slivers: [
        listView,
        listView,
      ],
    );
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("自定义滚动的view嵌套滑动")),
      body: Column(
        children: [
          Text("不做处理,下面两滑动互不干扰"),
          Flexible(flex: 1, child: buildTwoListView()),
          Text("做处理,下面两滑动嵌套滑动"),
          Flexible(flex: 1, child: buildTwoSliverList()),
          TextButton(
            child: Text("跳转自定义的CustomScroll"),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return _CustomScrollViewUseWidget();
              }));
            },
          ),
          TextButton(
            child: Text(
                "SliverPersistentHeader 的功能是当滑动到 CustomScrollView 的顶部时，可以将组件固定在顶部。"),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return PersistentHeaderRoute();
              }));
            },
          ),
          TextButton(
            child: Text("自定义sliver控件-弹性控件"),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return CustomSliverBoundWidget();
              }));
            },
          ),
          TextButton(
            child: Text("NestedScrollView 它的功能时组合（协调）两个可滚动组件"),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return NestedScrollViewDemo();
              }));
            },
          ),
          TextButton(
            child: Text(" SliverAppBar 在回到屏幕的过程中，底下的列表项也同时往下滑相应的偏移 6.12.3"),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return NestedScrollViewDemoSliverAppBar();
              }));
            },
          ),

            TextButton(
            child: Text(" SliverAppBar2 在回到屏幕的过程中，底下的列表项也同时往下滑相应的偏移 6.12.3"),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return SnapAppBar2();
              }));
            },
          ),

          TextButton(
            child: Text(" 三个Tab，为了获得更大的商品显示空间，我们希望用户向上滑动时 导航栏能够滑出屏幕，当用户向下滑动时，导航栏能迅速回到屏幕 6.12.4"),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return NestedTabBarView1();
              }));
            },
          ),

          TextButton(
            child: Text(" 三个Tab，状态保持"),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return TabPage();
              }));
            },
          ),
        ],
      ),
    );
  }
}

class _CustomScrollViewUseWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _CustomScrollViewUseWidgetState();
  }
}

class _CustomScrollViewUseWidgetState
    extends State<_CustomScrollViewUseWidget> {
  @override
  Widget build(BuildContext context) {
    return Material(
      child: CustomScrollView(
        physics: const BouncingScrollPhysics(
            parent: AlwaysScrollableScrollPhysics()), //弹性效果
        slivers: <Widget>[
          // AppBar，包含一个导航栏.
          SliverAppBar(
            pinned: true, // 滑动到顶端时会固定住
            expandedHeight: 250.0,
            flexibleSpace: FlexibleSpaceBar(
              title: const Text('Demo'),
              background: Image.asset(
                "ic_bind_card_focus.png",
                fit: BoxFit.cover,
              ),
            ),
          ),
          SliverPadding(
            padding: const EdgeInsets.all(8.0),
            sliver: SliverGrid(
              //Grid
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2, //Grid按两列显示
                mainAxisSpacing: 10.0,
                crossAxisSpacing: 10.0,
                childAspectRatio: 4.0,
              ),
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  //创建子widget
                  return Container(
                    alignment: Alignment.center,
                    color: Colors.cyan[100 * (index % 9)],
                    child: Text('grid item $index'),
                  );
                },
                childCount: 20,
              ),
            ),
          ),
          SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                //创建列表项
                return Container(
                  alignment: Alignment.center,
                  color: Colors.lightBlue[100 * (index % 9)],
                  child: Text('list item $index'),
                );
              },
              childCount: 20,
            ),
          ),

          SliverToBoxAdapter(
              child: SizedBox(
            width: 300,
            height: 300,
            child: ListView.builder(

                ///父子滚动方向不管是否一致 加上这个该布局拖动当前控件的时候 整体会滑动，当前控件不响应（类似事件消费）
                physics: NeverScrollableScrollPhysics(),
                itemCount: 20,

                ///方向不一致的时候 不加NeverScrollableScrollPhysics 拖动该布局 整体会滑动
                scrollDirection: Axis.horizontal,

                shrinkWrap: true,
                itemExtent: 50,
                //强制高度为50.0
                itemBuilder: (BuildContext context, int index) {
                  return ListTile(title: Text("$index"));
                }),
          )),

          /// SliverToBoxAdapter 组件，它是一个适配器：可以将 RenderBox 适配为 Sliver。
          /// 因为CustomScrollView 中的子控件必须要是Sliver
          SliverToBoxAdapter(
              child: Container(
            color: Color(0xFF13D0C0),
            child: SizedBox(
              height: 300,
              child: PageView(
                children: [
                  Center(child: Text("我是pagerview相当于viewpager")),
                  Text("2")
                ],
              ),
            ),
          )),
        ],
      ),
    );
  }
}

///
///SliverPersistentHeader 的功能是当滑动到 CustomScrollView 的顶部时，可以将组件固定在顶部。
///
class PersistentHeaderRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Material(
        child: CustomScrollView(
      slivers: [
        buildSliverList(),
        SliverPersistentHeader(
          pinned: true,
          delegate: SliverHeaderDelegate(
            //有最大和最小高度
            maxHeight: 80,
            minHeight: 50,
            child: buildHeader(1),
          ),
        ),
        buildSliverList(),
        SliverPersistentHeader(
          pinned: false,
          delegate: SliverHeaderDelegate.fixedHeight(
            //固定高度
            height: 50,
            child: buildHeader(2),
          ),
        ),
        buildSliverList(20),
        SliverPersistentHeader(
          pinned: true,
          delegate: SliverHeaderDelegate.builder(
            //固定高度
            maxHeight: 80,
            minHeight: 50,
            builder: (a, b, c) => buildHeader(3),
          ),
        ),
        buildSliverList(20),
      ],
    ));
  }

  // 构建固定高度的SliverList，count为列表项属相
  Widget buildSliverList([int count = 5]) {
    return SliverFixedExtentList(
      itemExtent: 50,
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          return ListTile(title: Text('$index'));
        },
        childCount: count,
      ),
    );
  }

  // 构建 header
  Widget buildHeader(int i) {
    return Container(
      color: Colors.lightBlue.shade200,
      alignment: Alignment.centerLeft,
      child: Text("PersistentHeader $i"),
    );
  }
}

typedef SliverHeaderBuilder = Widget Function(
    BuildContext context, double shrinkOffset, bool overlapsContent);

class SliverHeaderDelegate extends SliverPersistentHeaderDelegate {
  // child 为 header
  ///构造函数1，当 ： 符号后面是为了赋初始值
  SliverHeaderDelegate({
    required this.maxHeight,
    this.minHeight = 0,
    required Widget child,
  })  : builder = ((a, b, c) => child),
        assert(minHeight <= maxHeight && minHeight >= 0);

  ///构造函数2
  //最大和最小高度相同--特殊的构造函数
  ///https://blog.csdn.net/weixin_43294560/article/details/105149995
  SliverHeaderDelegate.fixedHeight({
    required double height,
    required Widget child,
  })  : builder = ((a, b, c) => child),
        maxHeight = height,
        minHeight = height;

  ///构造函数3
  //需要自定义builder时使用
  SliverHeaderDelegate.builder({
    required this.maxHeight,
    this.minHeight = 0,
    required this.builder,
  });

  final double maxHeight;
  final double minHeight;
  final SliverHeaderBuilder builder;

  @override
  Widget build(
    BuildContext context,
    double shrinkOffset,
    bool overlapsContent,
  ) {
    Widget child = builder(context, shrinkOffset, overlapsContent);

    ///当有多个 SliverPersistentHeader时，需要注意 ***第一个*** SliverPersistentHeader 的 overlapsContent 值会一直为 false。
    ///前提 是从第一个 SliverPersistentHeader 的参数 pinned: true,
    print('${child.key}: shrink: $shrinkOffset，overlaps:$overlapsContent');
    //测试代码：如果在调试模式，且子组件设置了key，则打印日志
    assert(() {
      if (child.key != null) {
        print('${child.key}: shrink: $shrinkOffset，overlaps:$overlapsContent');
      }
      return true;
    }());

    // 让 header 尽可能充满限制的空间；宽度为 Viewport 宽度，
    // 高度随着用户滑动在[minHeight,maxHeight]之间变化。
    return SizedBox.expand(child: child);
  }

  @override
  double get maxExtent => maxHeight;

  @override
  double get minExtent => minHeight;

  @override
  bool shouldRebuild(SliverHeaderDelegate old) {
    return old.maxExtent != maxExtent || old.minExtent != minExtent;
  }
}

///
///自定义Sliver，弹性相关控件
///
class CustomSliverBoundWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Material(
        child: CustomScrollView(
      //为了能使CustomScrollView拉到顶部时还能继续往下拉，必须让 physics 支持弹性效果
      physics:
          const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
      slivers: [
        //我们需要实现的 SliverFlexibleHeader 组件
        SliverFlexibleHeader(
          visibleExtent: 100, // 初始状态在列表中占用的布局高度
          // 为了能根据下拉状态变化来定制显示的布局，我们通过一个 builder 来动态构建布局。
          builder: (context, availableHeight, direction) {
            return GestureDetector(
              onTap: () => print('测试是否可以响应事件tap'), //测试是否可以响应事件
              child: Container(height: 150, color: Color(0xff16e5c6)),
            );
          },
        ),
        // 构建一个list
        buildSliverList(30),
      ],
    ));
  }

  // 构建固定高度的SliverList，count为列表项属相
  Widget buildSliverList([int count = 5]) {
    return SliverFixedExtentList(
      itemExtent: 50,
      delegate: SliverChildBuilderDelegate(
        (context, index) {
          return ListTile(title: Text('$index'));
        },
        childCount: count,
      ),
    );
  }
}

class _SliverFlexibleHeader extends SingleChildRenderObjectWidget {
  const _SliverFlexibleHeader({
    Key? key,
    required Widget child,
    this.visibleExtent = 0,
  }) : super(key: key, child: child);
  final double visibleExtent;

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _FlexibleHeaderRenderSliver(visibleExtent);
  }

  @override
  void updateRenderObject(
      BuildContext context, _FlexibleHeaderRenderSliver renderObject) {
    renderObject..visibleExtent = visibleExtent;
  }
}

class _FlexibleHeaderRenderSliver extends RenderSliverSingleBoxAdapter {
  _FlexibleHeaderRenderSliver(double visibleExtent)
      : _visibleExtent = visibleExtent;

  double _lastOverScroll = 0;
  double _lastScrollOffset = 0;
  late double _visibleExtent = 0;

  set visibleExtent(double value) {
    // 可视长度发生变化，更新状态并重新布局
    if (_visibleExtent != value) {
      _lastOverScroll = 0;
      _visibleExtent = value;
      markNeedsLayout();
    }
  }

  @override
  void performLayout() {
    // 滑动距离大于_visibleExtent时则表示子节点已经在屏幕之外了
    print("下拉值变化2constraints.scrollOffset：$constraints");
    if (child == null || (constraints.scrollOffset > _visibleExtent)) {
      geometry = SliverGeometry(scrollExtent: _visibleExtent);
      return;
    }

    // 测试overlap,下拉过程中overlap会一直变化.
    double overScroll = constraints.overlap < 0 ? constraints.overlap.abs() : 0;
    print("下拉值变化：$overScroll");
    var scrollOffset = constraints.scrollOffset;

    // 在Viewport中顶部的可视空间为该 Sliver 可绘制的最大区域。
    // 1. 如果Sliver已经滑出可视区域则 constraints.scrollOffset 会大于 _visibleExtent，
    //    这种情况我们在一开始就判断过了。
    // 2. 如果我们下拉超出了边界，此时 overScroll>0，scrollOffset 值为0，所以最终的绘制区域为
    //    _visibleExtent + overScroll.
    double paintExtent = _visibleExtent + overScroll - constraints.scrollOffset;
    // 绘制高度不超过最大可绘制空间
    paintExtent = min(paintExtent, constraints.remainingPaintExtent);

    //对子组件进行布局，关于 layout 详细过程我们将在本书后面布局原理相关章节详细介绍，现在只需知道
    //子组件通过 LayoutBuilder可以拿到这里我们传递的约束对象（ExtraInfoBoxConstraints）
    child!.layout(
      constraints.asBoxConstraints(maxExtent: paintExtent),
      parentUsesSize: false,
    );

    //最大为_visibleExtent，最小为 0
    double layoutExtent = min(_visibleExtent, paintExtent);

    //设置geometry，Viewport 在布局时会用到
    geometry = SliverGeometry(
      scrollExtent: layoutExtent,
      paintOrigin: -overScroll,
      paintExtent: paintExtent,
      maxPaintExtent: paintExtent,
      layoutExtent: layoutExtent,
    );
  }
}

typedef SliverFlexibleHeaderBuilder = Widget Function(
  BuildContext context,
  double maxExtent,
  ScrollDirection direction,
);

class SliverFlexibleHeader extends StatelessWidget {
  const SliverFlexibleHeader({
    Key? key,
    this.visibleExtent = 0,
    required this.builder,
  }) : super(key: key);

  final SliverFlexibleHeaderBuilder builder;
  final double visibleExtent;

  @override
  Widget build(BuildContext context) {
    return _SliverFlexibleHeader(
      visibleExtent: visibleExtent,
      child: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          return builder(
              context, constraints.maxHeight, ScrollDirection.reverse);
        },
      ),
    );
  }
}
